﻿using ImageDomain.Ob;
using itk.simple;
using OpenCvSharp;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;

namespace ImageDomain.Utils;
public class WriteUtil
{
    #region Singleton 懒加载单例模式
    private WriteUtil() { }
    private static readonly Lazy<WriteUtil> Lazy
        = new Lazy<WriteUtil>(() => new WriteUtil());
    public static WriteUtil Instance => Lazy.Value;

    #endregion

    public void WriteToDisk(Image itkImage, string fileName)
    {
        if (!fileName.Contains(".mha") && !fileName.Contains(".mhd") && !fileName.Contains("nii.gz"))
            throw new Exception("仅支持 mha  mhd  nii.gz 格式");

        //itkImage.SetDirection(new VectorDouble() { 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 });
        SimpleITK.WriteImage(itkImage, fileName);
    }

    public async Task WriteToDiskAsync(Image itkImage, string fileName)
    {
        await Task.Run(() =>
        {
            WriteToDisk(itkImage, fileName);
        });
    }

    /// <summary>
    /// 将dcm序列保存到本地
    /// </summary>
    /// <param name="dcmFiles"></param>
    /// <param name="fileName"></param>
    public void SaveDcmToVolume(IList<string>? dcmFiles, string fileName)
    {
        if (dcmFiles == null || dcmFiles.Count < 1) return;

        var image = SimpleITK.ReadImage(dcmFiles.First(),
            PixelIDValueEnum.sitkInt32);

        var fileList = (from file in dcmFiles let tempImage = SimpleITK.ReadImage(file, PixelIDValueEnum.sitkInt32) 
            let inNum = tempImage.GetMetaData("0020|0013") select new SortDcm() { InstanceNumber = int.Parse(inNum), FileName = file }).ToList();

        var sortFiles = fileList.OrderBy(fi => fi.InstanceNumber).Select(fi => fi.FileName).ToList();

        var itkImage = SimpleITK.ReadImage(new VectorString(sortFiles), PixelIDValueEnum.sitkInt32);

        foreach (var key in image.GetMetaDataKeys())
        {
            itkImage.SetMetaData(key, image.GetMetaData(key));
        }

        WriteToDisk(itkImage, fileName);
    }

    public async Task SaveDcmToVolumeAsync(IList<string>? dcmFiles, string fileName)
    {
        await Task.Run(() => { SaveDcmToVolume(dcmFiles, fileName);});
    }


    /// <summary>
    /// 将绘制的图像 写入本地
    /// </summary>
    /// <param name="file"></param>
    /// <param name="mats"> 这里的mat尽量转换成 灰度图像 </param>
    /// <param name="spacing"></param>
    /// <param name="direction"></param>
    /// <param name="origin"></param>
    public void WriteDrawMatToVolumn(
        [NotNull] string file,
        [NotNull] IReadOnlyList<Mat> mats,
        [NotNull] IReadOnlyList<double> spacing,
        IReadOnlyList<double> direction = null,
        IReadOnlyList<double> origin = null)
    {
        var first = mats[0];
        var width = (uint)first.Width;
        var height = (uint)first.Height;
        var depth = (uint)mats.Count;
        var data = new int[width * height * depth];

        for (var i = 0; i < mats.Count; i++)
        {
            var temp = mats[i];
            if (temp.Channels() > 1)
                temp = temp.CvtColor(ColorConversionCodes.BGRA2GRAY);

            // 最大最小归一化处理
            temp = temp.Normalize(0, 100, NormTypes.MinMax);

            temp.ConvertTo(temp, MatType.CV_32SC1);

            Marshal.Copy(temp.Data, data, (int)(i * width * height), (int)(width * height));
        }

        var saveImage = SimpleITK.ImportAsInt32(
            Marshal.UnsafeAddrOfPinnedArrayElement(data, 0),
            new VectorUInt32(new[] { width, height, depth }));
        var originTemp = origin ?? new[] { 0.0, 0.0, 0.0 };
        var directionTemp = direction ?? new[] { 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 };
        saveImage.SetOrigin(new VectorDouble(originTemp));
        saveImage.SetSpacing(new VectorDouble(spacing));
        saveImage.SetDirection(new VectorDouble(directionTemp));
        SimpleITK.WriteImage(saveImage, file);
    }

    public async Task WriteDrawMatToVolumnAsync([NotNull] string file,
        [NotNull] IReadOnlyList<Mat> mats,
        [NotNull] IReadOnlyList<double> spacing,
        IReadOnlyList<double> direction = null,
        IReadOnlyList<double> origin = null)
    {
        await Task.Run(() => { WriteDrawMatToVolumn(file, mats, spacing, direction, origin);});
    }


    /// <summary>
    /// 
    /// </summary>
    /// <param name="cvMats"></param>
    /// <param name="spacing"></param>
    /// <param name="origin"></param>
    /// <param name="direction"></param>
    /// <returns></returns>
    public Image ConvertCvMaskMatsToImage(IList<Mat> cvMats, IReadOnlyList<double> spacing, IReadOnlyList<double> origin, IReadOnlyList<double> direction)
    {
        var width = cvMats[0].Width;
        var height = cvMats[0].Height;
        var count = cvMats.Count;

        var imageData = new byte[width * height * count];
        for (var i = 0; i < count; i++)
        {
            var mat = cvMats[i];
            if (mat.Channels() > 1)
                mat = mat.CvtColor(ColorConversionCodes.BGRA2GRAY);
            Marshal.Copy(mat.Data, imageData, i * width * height, width * height);
        }

        var itkImage = new Image((uint)width, (uint)height, (uint)count, PixelIDValueEnum.sitkUInt8);
        itkImage.SetSpacing(new VectorDouble(spacing));
        itkImage.SetOrigin(new VectorDouble(origin));
        itkImage.SetDirection(new VectorDouble(direction));
        var ptr = itkImage.GetBufferAsVoid();
        Marshal.Copy(imageData, 0, ptr, imageData.Length);
        return SimpleITK.Cast(itkImage, PixelIDValueEnum.sitkInt32);
    }


}
